package com.example.capsule_chat0322.netty;

import android.content.Context;
import android.util.Log;

import com.example.capsule_chat0322.bean.MessageBean;
import com.example.capsule_chat0322.configuration.ActionType;
import com.example.capsule_chat0322.configuration.Constant;
import com.example.capsule_chat0322.global.GlobalAssistMap;
import com.example.capsule_chat0322.global.GlobalThreadPool;
import com.example.capsule_chat0322.listener.NettyGroupMessageListener;
import com.example.capsule_chat0322.protocol.MessageBeanDecoder;
import com.example.capsule_chat0322.protocol.MessageBeanEncoder;
import com.example.capsule_chat0322.protocol.MessageBeanProtocol;
import com.example.capsule_chat0322.request.RegisterRequest;
import com.example.capsule_chat0322.utils.AlterUtils;
import com.google.gson.Gson;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;

/**
 * @Author: zhuzhiming
 * @Date: 2022/4/27 20:15
 */
public class NettyManage {

    private volatile boolean isConnect = false;
    private NioEventLoopGroup clientLoop;
    private Bootstrap bootstrap;
    private ChannelFuture future;
    private Channel channel;
    private int reCount = 30;
    private Context context;
    private boolean hasFail = false;
    private Map<Integer, MessageBean> noSendMessageMap = new HashMap<>();
    private List<MessageBean> noSendMessageList = new ArrayList<>();
    private ThreadPoolExecutor threadPool = GlobalThreadPool.buildThreadPool();
    private long startTime;
    private volatile int noSendIndex = 0;
    private boolean hasRegister = false;
    private ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);
    private final NettyGroupMessageListener messageListener;


    public NettyManage(NettyGroupMessageListener messageListener) {
        this.messageListener = messageListener;

    }


    public void connect(String host, int port, Context context) throws InterruptedException {

        this.context = context;

        if (!isConnect) {

            synchronized (NettyManage.class) {

                if (!isConnect) {
                    reCount--;
                    clientLoop = new NioEventLoopGroup();
                    bootstrap = new Bootstrap();

                    bootstrap.group(clientLoop)
                            .channel(NioSocketChannel.class)
                            .handler(new ChannelInitializer<SocketChannel>() {
                                @Override
                                protected void initChannel(SocketChannel ch) throws Exception {
                                    ch.pipeline()
                                            .addLast("encode", new MessageBeanEncoder())
                                            .addLast("decoder", new MessageBeanDecoder())
                                            .addLast(new IdleStateHandler(60, 60, 120, TimeUnit.SECONDS))
                                            .addLast(new ClientNettyHandler(messageListener));
                                }
                            });
                    try {

                        future = bootstrap.connect(host, port).addListener(new ChannelFutureListener() {
                            @Override
                            public void operationComplete(ChannelFuture future) throws Exception {
                                if (future.isSuccess()) {
                                    if (hasFail) {
                                        hasFail = false;
                                        threadPool.execute(new Runnable() {
                                            @Override
                                            public void run() {
                                                AlterUtils.alertByToastInSonThread(context, "连接成功~~");
                                            }
                                        });
                                    }
                                    isConnect = true;
                                    channel = future.channel();
                                    //TODO 向服务器注册自己
                                    if (!hasRegister) {
                                        doRegister();
                                        if (GlobalAssistMap.getObject("userId") != null) {
                                            hasRegister = true;


                                        }
                                    }

                                } else {
                                    isConnect = false;
                                    hasFail = true;
                                }
                                System.setProperty("isConnect", String.valueOf(isConnect));
                            }
                        }).sync();

                        future.channel().closeFuture().sync();

                    } catch (Exception e) {
                        Log.i("error", e.toString());
                    } finally {
                        clientLoop.shutdownGracefully();
                        isConnect = false;
                        hasFail = true;
                        System.setProperty("isConnect", String.valueOf(isConnect));
                        if (reCount >= 0) {
                            Thread.sleep(1500);
                            threadPool.execute(new Runnable() {
                                @Override
                                public void run() {
                                    if (reCount % 5 == 0) {
                                        AlterUtils.alertByToastInSonThread(context, "连接中断,正在尝试重新连接~~");
                                    }
                                }
                            });
                            reConnect();
                        } else {
                            threadPool.execute(new Runnable() {
                                @Override
                                public void run() {
                                    AlterUtils.alertByToastInSonThread(context, "多次尝试连接失败,请稍后再试~~");
                                }
                            });
                        }

                    }

                }

            }

        }

    }


    private void reConnect() throws InterruptedException {

        connect(Constant.HOST, Constant.PORT, context);

    }


    public void doSendMessage(MessageBean messageBean, ChannelFutureListener listener) throws InterruptedException {

        if (!isConnect) {
            reCount = 10;
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        reConnect();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        if (!isConnect) {
            noSendMessageList.add(messageBean);
            dealNoSendMessage();
            return;
        }
        String json = new Gson().toJson(messageBean);
        MessageBeanProtocol messageBeanProtocol = new MessageBeanProtocol();
        messageBeanProtocol.setLen(json.getBytes(StandardCharsets.UTF_8).length);
        messageBeanProtocol.setContent(json.getBytes(StandardCharsets.UTF_8));
        channel.writeAndFlush(messageBeanProtocol).addListener(listener);
    }

    public void doShutdown() {
        if (isConnect) {
            clientLoop.shutdownGracefully();
            isConnect = false;
        }
    }

    public void dealNoSendMessage() {

        scheduledThreadPool.schedule(new Runnable() {
            @Override
            public void run() {
                while (noSendMessageList.size() > 0) {
                    if (isConnect) {
                        Gson gson = new Gson();
                        MessageBeanProtocol messageBeanProtocol = new MessageBeanProtocol();
                        while (noSendMessageList.size() > 0) {
                            MessageBean messageBean = noSendMessageList.get(0);
                            String messageBeanJson = gson.toJson(messageBean);
                            messageBeanProtocol.setLen(messageBeanJson.getBytes(StandardCharsets.UTF_8).length);
                            messageBeanProtocol.setContent(messageBeanJson.getBytes(StandardCharsets.UTF_8));
                            channel.writeAndFlush(messageBeanJson);
                            noSendMessageList.remove(0);
                        }
                    }
                }
            }
        }, 2000, TimeUnit.MILLISECONDS);

    }

    public void doRegister() {

        String imei = (String) GlobalAssistMap.getObject("imei");
        if (imei == null) {
            return;
        }

        MessageBean messageBean = new MessageBean();
        messageBean.setActionType(ActionType.REGISTER_TYPE);
        messageBean.setImei(imei);
        try {
            NettyClient.getInstance().nettyManage.doSendMessage(messageBean, new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                }
            });
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        RegisterRequest.doRegister();

    }
}
